#!/usr/bin/php
<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 2018-08-21 0021
 * Time: 上午 10:57
 */
error_reporting(E_ALL & E_ERROR);

class Console extends BaseConsole {
    protected $_checkArguments = true;
    protected $_allowArgumentsList = [
            '--', '--process-count', '--unique-key', '--unique', '--stop', '--start'
        ];
    
    public function main() {
        $processCount = $this->getArgInt('--process-count', 1);
        $command = $this->getArg('--');
        $unique = $this->getArg('--unique');
        $key = $this->getArg('--unique-key');
        
        if($this->issetArg('--stop')) {
            $this->stopAction();
        }
        
        if($this->issetArg('--start')) {
            $this->startAction();
        }
        
        $unique = trim($unique);
        $key = trim($key);
        $command = trim($command);
        $command = rtrim($command, '&');
    
        if(empty($unique)) {
            $this->error('缺少unique');
        }
        
        if(empty($key)) {
            $this->error('缺少unique-key');
        }
    
        if($this->_isLock($unique)) {
            echo "'$unique' is locked. use '--start=$unique' argument to unlock\n";
            return ;
        }
        
        $searchValue = "$unique,daemonrun-subprocess";
        $searchCommand = "ps -aux | grep {$searchValue} | grep -v ' grep '";
    
        $this->_removeRedirectOutput($command, '>>');
        $this->_removeRedirectOutput($command, '>');
        $command .= " $key$searchValue ";
        $command = "$command > /dev/null ";
    
    
        if(substr(strrev($command), 0,1) != '&') {
            $command .= '&';
        }
        
        exec($searchCommand, $result);
        if(is_array($result)) {
            $currentCount = count($result);
        } else {
            $currentCount = count(array_filter(explode("\n", trim($result))));
        }
        
        $lastCount = $processCount-$currentCount;
        
        for($i=0; $i<$lastCount; $i++) {
            echo "> $command \n";
            system("$command");
        }
    }
    
    protected function stopAction() {
        $dir = __DIR__;
        $stop = $this->getArg('--stop');
        $allKeys = array_filter(array_map('trim', explode(',', $stop)));
        
        foreach ($allKeys as $key) {
            file_put_contents("$dir/" . $this->_getLockName($key), '1');
        }
        
        exit;
    }
    
    protected function startAction() {
        $dir = __DIR__;
        $start = $this->getArg('--start');
        $allKeys = array_filter(array_map('trim', explode(',', $start)));
    
        foreach ($allKeys as $key) {
            unlink("$dir/" . $this->_getLockName($key));
        }
        
        exit;
    }
    
    protected function _getLockName($key) {
        return ".$key.lock";
    }
    
    protected function _isLock($key) {
        return file_exists(__DIR__ . "/" . $this->_getLockName($key));
    }
}

new Console();

abstract class BaseConsole {
    protected $_checkArguments = false;
    protected $_allowArgumentsList = [];
    
    const I18N_ARG_NOT_SUPPORT = 'I18N_ARG_NOT_SUPPORT';
    const I18N_ARG_ALREADY_EXIST = 'I18N_ARG_ALREADY_EXIST';
    
    protected $_i18n = [
        self::I18N_ARG_NOT_SUPPORT => "不支持的参数 '%s'",
        self::I18N_ARG_ALREADY_EXIST => "短参数 '%s' 已经存在",
    ];
    
    protected $_arguments = [];
    
    public function __construct() {
        $this->_parseArguments();
        $this->_checkArguments();
        $this->main();
    }
    
    public function main() {}
    
    public function _baseOutput($args=[]) {
        echo call_user_func_array('sprintf', $args);
    }
    
    public function __($code) {
        return isset($this->_i18n[$code]) ? $this->_i18n[$code] : $code;
    }
    
    public function error($msg='') {
        $args = func_get_args();
        $args[0] = "[ERROR] {$this->__($msg)}\n";
        $this->_baseOutput($args);
        die;
    }
    
    public function msg($msg='') {
        $args = func_get_args();
        $args[0] = "{$this->__($msg)}\n";
        $this->_baseOutput($args);
        die;
    }
    
    public function warning($msg='') {
        $args = func_get_args();
        $args[0] = "[WARNING] {$this->__($msg)}\n";
        $this->_baseOutput($args);
        die;
    }
    
    protected function _checkArguments() {
        if(!$this->_checkArguments) {
            return;
        }
        
        foreach (array_keys($this->_arguments) as $key) {
            if(!in_array($key, $this->_allowArgumentsList)) {
                $this->error(self::I18N_ARG_NOT_SUPPORT, $key);
            }
        }
        
        return;
    }
    
    protected function _parseArguments() {
        $args = $_SERVER['argv'];
        unset($args[0]);
        $pointer = null;
        $count = count($args);
        $args = array_values($args);
        
        for($i=0; $i<$count; $i++) {
            $arg = $args[$i];
            
            if($arg == '--') {
                $this->_arguments['--'] = implode(' ', $this->_processingPassArgs(array_splice($args, $i+1)));
                break;
            }
            
            if($arg[0] == '-') {
                if($arg[1] == '-') {
                    $arg = ltrim($arg, '-');
                    list($key, $val) = explode('=', $arg, 2);
                    
                    if(in_array($val[0], ['\'"'])) {
                        trim($val, $val[0]);
                    }
                    
                    $this->_arguments["--$key"] = $val;
                } else {
                    $arg = ltrim($arg, '-');
                    $keys = str_split($arg);
                    
                    foreach ($keys as $key) {
                        $saveKey = "-$key";
                        
                        if(isset($this->_arguments[$saveKey])) {
                            $this->error(self::I18N_ARG_ALREADY_EXIST, $key);
                        } else {
                            $this->_arguments [$saveKey] = true;
                        }
                    }
                }
            }
        }
    }
    
    public function issetArg($key) {
        return isset($this->_arguments[$key]);
    }
    
    public function getArg($key, $default='') {
        $val = $this->_arguments[$key];
        
        if($val === null || $val === false) {
            return $default;
        }
        
        return $val;
    }
    
    public function getArgInt($key, $default=0) {
        return intval($this->getArg($key, $default));
    }
    
    public function getArgFloat($key, $default=0) {
        return floatval($this->getArg($key, $default));
    }
    
    protected function _processingPassArgs($args) {
        $oriArg = $args;
        
        if(!is_array($oriArg)) {
            $args = [$args];
        }
        
        foreach($args as $i => $arg) {
            if(strpos($arg, ' ') !== false) {
                if($arg[0] == "\"") {
                    $args [$i] = "'$arg'";
                } else {
                    $args [$i] = "\"$arg\"";
                }
            }
        }
        
        if(is_array($oriArg)) {
            return $args;
        } else {
            return $args[0];
        }
    }
    
    protected function _removeRedirectOutput($command, $reChar='>>') {
        $commandPro = strrev($command);
        $rcpos = strpos($commandPro, $reChar);
        
        if($rcpos === false) {
            return $command;
        }
        
        list($tail, $head) = explode($reChar, $commandPro, 2);
        $isInStr = false;
        
        foreach(str_split($tail) as $char) {
            if($char == "'") {
                if($isInStr == $char) {
                    $isInStr = false;
                } else if(!$isInStr) {
                    $isInStr = $char;
                }
            }
            
            if($char == "\"") {
                if($isInStr == $char) {
                    $isInStr = false;
                } else if(!$isInStr) {
                    $isInStr = $char;
                }
            }
        }
        
        if($isInStr) {
            return $command;
        }
        
        $head = ltrim($head);
        
        if(preg_match('#^\s*[0-9]\s+#', $head)) {
            $head = preg_replace('#^\s*[0-9]\s+#', '', $head);
        }
        
        $head = strrev($head);
        return $this->_removeRedirectOutput($head, $reChar);
    }
    
    protected function _reformatCommand($cmd) {
        $command = [];
        $pos = -1;
        
        while(false !== ($pos = strpos($cmd, ' ', $pos+1))) {
            if(!$this->_isCharInString($pos, $cmd)) {
                $command []= substr($cmd, 0, $pos);
                $cmd = substr($cmd, $pos+1);
            }
        }
        
        $command []= $cmd;
        $command = array_filter(array_map('trim',$command));
        return implode(' ', $command);
    }
    
    protected function _isCharInString($pos, $str) {
        $len = strlen($str);
        $isInStr = false;
        
        for($i=0; $i<$pos; $i++) {
            if($i >= $len) {
                break;
            }
            
            $char = $str[$i];
            
            if($char == "'") {
                if($isInStr == $char) {
                    $isInStr = false;
                } else if(!$isInStr) {
                    $isInStr = $char;
                }
            }
            
            if($char == "\"") {
                if($isInStr == $char) {
                    $isInStr = false;
                } else if(!$isInStr) {
                    $isInStr = $char;
                }
            }
        }
        
        return $isInStr;
    }
    
}
